package com.yfree.global

import com.alibaba.csp.sentinel.slots.block.BlockException
import com.google.gson.Gson
import com.google.gson.internal.LinkedTreeMap
import com.yfree.config.YConfig
import com.yfree.model.YBaseModel
import com.yfree.model.YModel
import com.yfree.model.YRootModel
import com.yfree.utils.YAESCrypt
import io.seata.core.context.RootContext
import io.seata.tm.api.GlobalTransactionContext
import org.redisson.api.RLock
import org.redisson.api.RedissonClient
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.transaction.interceptor.TransactionAspectSupport
import java.nio.charset.StandardCharsets

open class YController {
    var gson = Gson()
    private var yLog = LoggerFactory.getLogger("YFreeLog")

    @Autowired
    private lateinit var redisson: RedissonClient

    fun <T> yParams(jkparam: Any?, classOfT: Class<T>): T {
        val param = jkparam ?: "{}"
        val json = if (YConfig.Encrypt.enable) YAESCrypt.decrypt(param.toString(), YConfig.Encrypt.key) else param
        return gson.fromJson(json.toString(), classOfT)
    }

    fun yResult(code: Int = YConfig.Response.successCode, result: List<YModel> = listOf(), message: String = YConfig.Response.successMsg, rollback: Boolean = true): String {
        if (rollback && code != YConfig.Response.successCode) yRollback()
        val json = gson.toJson(YBaseModel(YRootModel("$code", message, result)))
        return if (YConfig.Encrypt.enable) YAESCrypt.encrypt(json, YConfig.Encrypt.key) else json
    }

    fun <T : YModel> yFromRPC(json: String?, clazz: Class<T>, errorMessage: String? = null, successCode: HttpStatus = HttpStatus.OK, throwWhenFail: Boolean = true): YBaseModel<T> {
        val yModel = gson.fromJson<YBaseModel<T>>(json, YBaseModel::class.java)
        val ts = arrayListOf<T>()
        val map = gson.fromJson(json, Map::class.java)
        val result = (map["root"] as Map<String, Any>)["result"] as List<LinkedTreeMap<String, Any>>
        result.forEach {
            ts.add(gson.fromJson(gson.toJson(it), clazz))
        }
        yModel.root.result = ts
        if (throwWhenFail && yModel.root.code != successCode.value().toString()) throw YError(errorMessage ?: yModel.root.message)
        return yModel
    }

    fun yFromRPC(json: String?, errorMessage: String? = null, successCode: HttpStatus = HttpStatus.OK, throwWhenUnSuccess: Boolean = true): YBaseModel<YModel> {
        val yModel = gson.fromJson<YBaseModel<YModel>>(json, YBaseModel::class.java)
        if (throwWhenUnSuccess && yModel.root.code != successCode.value().toString()) throw YError(errorMessage ?: yModel.root.message)
        return yModel
    }

    fun YError(message: String = YConfig.Response.yErrorDefaultMessage) = RuntimeException(message)

    fun yRollback(rollback: Boolean = true, rollback_global: Boolean = true) {
        if (rollback)
            try {
                try {
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()
                } catch (e: Exception) {
                }
            } catch (e: Error) {
            }
        if (rollback_global)
            try {
                try {
                    GlobalTransactionContext.reload(RootContext.getXID()).rollback()
                } catch (e: Exception) {
                }
            } catch (e: Error) {
            }
    }

    fun <T> yRunLock(key: String, work: (lock: RLock) -> T): T {
        val lock = redisson.getLock("${this.javaClass.canonicalName}_$key")
        lock.lock()
        val result = work.invoke(lock)
        lock.unlock()
        return result
    }

    fun yLog(message: String) {
        if (YConfig.DEBUG) yLog.error(message)
    }

    fun yGetResponseByException(e: Exception): ResponseEntity<String> {
        val headers = HttpHeaders()
        headers.set("Content-Type", "application/json; charset=${StandardCharsets.UTF_8.name()}")
        return ResponseEntity(yResult(code = HttpStatus.INTERNAL_SERVER_ERROR.value(), message = (if (YConfig.DEBUG) e.localizedMessage else YConfig.Response.yErrorDefaultMessage)), headers, HttpStatus.INTERNAL_SERVER_ERROR)
    }

    fun yfree_defaultFallback(e: Throwable) = yResult(code = YConfig.Response.fallBackCode, message = if (YConfig.DEBUG) e.localizedMessage!! else YConfig.Response.defaultFallback)
    fun yfree_defaultFallback1(p1: String, e: Throwable) = yfree_defaultFallback(e)
    fun yfree_defaultFallback2(p1: String, p2: String, e: Throwable) = yfree_defaultFallback(e)
    fun yfree_defaultFallback3(p1: String, p2: String, p3: String, e: Throwable) = yfree_defaultFallback(e)
    fun yfree_defaultBlockHandle(e: BlockException) = yResult(code = YConfig.Response.blockHandleCode, message = if (YConfig.DEBUG) e.localizedMessage!! else YConfig.Response.defaultBlockHandle)
}
